﻿/*
 * KRPC Host
 * 
 * See more details in BitTorrentSharp.Modeling.KRPC
 * 
 * Author: snowdreamist
 * Date: 2011/11/24
 * 
 */

using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Net;
using System.Linq;
using System.Text;
using BitTorrentSharp.KRPC.Network;
using BitTorrentSharp.KRPC.Messages;
using BitTorrentSharp.KRPC.Contract;
using BitTorrentSharp.KRPC.Handler;
using BitTorrentSharp.KRPC.Threading;

namespace BitTorrentSharp.KRPC
{
    public class KRPCHost
    {
        #region static
        /// <summary>
        /// Current
        /// </summary>
        [ThreadStatic]
        private static KRPCHost _current;
        /// <summary>
        /// Current host
        /// </summary>
        public static KRPCHost Current
        {
            get
            {
                return _current;
            }
            set
            {
                _current = value;
            }
        }

        #endregion

        public KRPCHost(
            IKRPCNetworkHandler networkHandler,
            IMessageDispatcher messageDispatcher,
            IMessageSerializerFactory messageSerializerFactory,
            IServiceSelector serviceSelector,
            IOperationSelector operationSelector,
            Type[] serviceContractTypes
            )
        {
            if (networkHandler == null)
                throw new ArgumentNullException("networkHandler");
            if (messageDispatcher == null)
                throw new ArgumentNullException("messageDispatcher");
            if (messageSerializerFactory == null)
                throw new ArgumentNullException("messageSerializerFactory");
            if (serviceSelector == null)
                throw new ArgumentNullException("serviceSelector");
            if (operationSelector == null)
                throw new ArgumentNullException("operationSelector");
            if (serviceContractTypes == null)
                throw new ArgumentNullException("serviceContractTypes");
            if (serviceContractTypes.Length == 0)
                throw new ArgumentException("Need service contract");
            // init
            // network
            this.NetworkHandler = networkHandler;
            this.NetworkHandler.DataReceived += new EventHandler<DataReceivedEventArgs>(NetworkHandler_DataReceived);
            // message
            this.MessageDispatcher = messageDispatcher;
            this.MessageSerializerFactoryFactory = new MessageSerializerFactoryFactory();
            this.MessageSerializerFactory = messageSerializerFactory;
            // services
            this.Services = new ReadOnlyCollection<KRPCService>(serviceContractTypes.Distinct().Select(p => new KRPCService(p)).ToList());
            // invoke
            this.Invoker = new InvokeHandler();
            this.AsyncInvokePool = new KRPCAsyncInvokePool();
        }

        #region components
        /// <summary>
        /// Network handler
        /// </summary>
        public IKRPCNetworkHandler NetworkHandler { get; private set; }
        /// <summary>
        /// Message dispatcher
        /// </summary>
        public IMessageDispatcher MessageDispatcher { get; private set; }
        /// <summary>
        /// Message serializer factory factory
        /// </summary>
        public MessageSerializerFactoryFactory MessageSerializerFactoryFactory { get; private set; }
        /// <summary>
        /// Message serializer factory
        /// </summary>
        public IMessageSerializerFactory MessageSerializerFactory { get; private set; }
        /// <summary>
        /// Service
        /// </summary>
        public ReadOnlyCollection<KRPCService> Services { get; private set; }
        /// <summary>
        /// Select a service
        /// </summary>
        public IServiceSelector ServiceSelector { get; private set; }
        /// <summary>
        /// Select an operation
        /// </summary>
        public IOperationSelector OperationSelector { get; private set; }
        /// <summary>
        /// Invoker
        /// </summary>
        public InvokeHandler Invoker { get; private set; }
        /// <summary>
        /// Async invoker
        /// </summary>
        public KRPCAsyncInvokePool AsyncInvokePool { get; private set; }

        #endregion

        #region private
        /// <summary>
        /// Network handler received data
        /// </summary>
        private void NetworkHandler_DataReceived(object sender, DataReceivedEventArgs e)
        {
           // prepare environment
            KRPCHost.Current = this;
            // send to message dispatcher
            this.MessageDispatcher.OnMessageIncoming(e.GetData(), e.RemoteEndPoint);
        }

        #endregion
    }
}
